//
// Created by QU on 2024/4/25.
//

#include <iostream>
#include <string>
#include <utility>          // for std::pair, std::move, std::ref, std::bind.........
#include <memory>

using namespace std;

// 如果它漏掉了move调用，将会使用string的拷贝构造函数。
// 其次，我们通常不为move提供一个using声明（参见3.1节，第74页），原因将在18.2.3节（第706页）中解释。
// 当我们使用move时，直接调用std::move而不是move.

int main([[maybe_unused]] int argc, [[maybe_unused]]char const * const argv[]) {
    std::cout << "hello" << endl;

}

// 模版函数中, 右值引用函数推断类型
//           引用折叠和右值引用参数

template <typename T>
void f3(T&& t);
// 我们可能认为f3(i)这样的调用是不成功的(i为int), 但是这确实可以调用.
// 其中包含两个隐藏的规则.
// 1. 我们将一个左值传递给函数的右值的引用参数, 而且右值引用指向模版类型参数的时候(T&&),
//      编译器推断模版类型的参数为实参的左值引用类型.
//          当我们调用f3(i), 编译器推断T的类型为 int&, 得到void f3<int &> (int& &&);
// 2. 如果我们间接创建一个引用的引用，则这些引用形成了“折叠”。
//      在所有情况下（除了一个例外），引用会折叠成一个普通的左值引用类型。
//      在新标准中，折叠规则扩展到右值引用。只在一种特殊情况下引用会折叠成右值引用：右值引用的右值引用。
//      X&&、X& &&和X&& &都折叠成类型 x&类型
//      x&& &&折叠成x&&


// 这两个规则导致了两个重要结果：
// ·如果一个函数参数是一个指向模板类型参数的右值引用（如，T&&），则它可以被绑定到一个左值；且
// ·如果实参是一个左值，则推断出的模板实参类型将是一个左值引用，且函数参数将被实例化为一个（普通）左值引用参数（T&）
// 另外值得注意的是，这两个规则暗示，我们可以将任意类型的实参传递给T&&类型的函数参数。
// 对于这种类型的参数，（显然）可以传递给它右值，而如我们刚刚看到的，也可以传递给它左值。

// 下面是std::move的定义.
template<typename Tp>
constexpr typename std::remove_reference<Tp>::type&&
move(Tp&& _t) noexcept
{
    return static_cast<typename std::remove_reference<Tp>::type&&>(_t);
}
// 如果传递的是右值,  比如 int&&, Tp为int, 通过static_cast转换成int&&;
// 如果传递的是左值, 解释称int& &&, Tp为int &, remove_reference为int, 返回类型仍然是int&&




